package com.qingcheng.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.qingcheng.dao.BrandMapper;
import com.qingcheng.dao.SpecMapper;
import com.qingcheng.service.goods.SkuSearchService;
import com.qingcheng.util.CacheKey;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SkuSearchServiceImpl implements SkuSearchService {


    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private BrandMapper brandMapper;

    @Autowired
    private SpecMapper specMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 根据map搜索
     *
     * @param searchMap
     * @return
     */
    @Override
    public Map search(Map<String, String> searchMap) {


        //1.封装查询请求
        //查询请求对象
        SearchRequest searchRequest = new SearchRequest("sku");
        searchRequest.types("doc");//设置查询的类型

        //查询源构建器
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //过滤查询构建器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        //匹配查询构建器 1.1关键字查询
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name",searchMap.get("keywords"));
        boolQueryBuilder.must(matchQueryBuilder);


        //1.3过滤查询(分类查询)
        if(searchMap.get("category")!=null) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("categoryName", searchMap.get("category"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //1.4过滤查询(品牌查询)
        if(searchMap.get("brand")!=null) {
            TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brandName", searchMap.get("brand"));
            boolQueryBuilder.filter(termQueryBuilder);
        }

        //1.5过滤查询(规格查询)
        for(String key: searchMap.keySet()) {
            if (key.startsWith("spec.")) {//如果是规格参数
                TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery(key+".keyword", key);
                boolQueryBuilder.filter(termQueryBuilder);
            }
        }

        //1.6范围查询(价格过滤)
        if(searchMap.get("price")!=null) {
            String[] prices = searchMap.get("price").split("-");//拆分价格 设置两个范围查询构建器 正常也可以写一起的QueryBuilders.rangeQuery("price").gte(prices[0]).lte(prices[1])
            if(prices[0]!=null){//设置最小值
                System.out.println(prices[0]);
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(prices[0]+"00");
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
            if(prices[1]!="*"){//设置最大值
                System.out.println(prices[1]);
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").lte(prices[1]+"00");
                boolQueryBuilder.filter(rangeQueryBuilder);
            }
        }

        //分页1.7

        int pageNo = Integer.parseInt(searchMap.get("pageNo"));//页码
        int pageSize = 30;//每页大小
        //起始记录下标
        int fromIndex = (pageNo-1)*pageSize;
        searchSourceBuilder.from(fromIndex);
        searchSourceBuilder.size(pageSize);


        //排序1.8
        String sort = searchMap.get("sort");//排序字段
        String sortOrder = searchMap.get("sortOrder");//排序规则
        if(!sort.equals("")){
            searchSourceBuilder.sort(sort, SortOrder.valueOf(sortOrder));
        }

        //高亮设置1.9
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name").preTags("<font style='color:red'>").postTags("</font>");
        searchSourceBuilder.highlighter(highlightBuilder);


        //聚合查询(1.2商品分类)
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("sku_category").field("categoryName");
        searchSourceBuilder.aggregation(termsAggregationBuilder);

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);

        //2.获取查询结果
        Map<String,Object> resultMap = new HashMap<String,Object>();
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchHits = searchResponse.getHits();
            SearchHit[] hits = searchHits.getHits();

            //2.1商品列表
            List<Map<String,Object>> resultList = new ArrayList<Map<String,Object>>();
            for(SearchHit hit:hits){
                Map<String, Object> skuMap = hit.getSourceAsMap();

                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField highlightField = highlightFields.get("name");
                Text[] fragments = highlightField.getFragments();
                skuMap.put("name",fragments[0].toString());//用高亮的name，替换不高亮的name

                resultList.add(skuMap);
            }
            resultMap.put("rows",resultList);

            //2.2商品分类
            Aggregations aggregations = searchResponse.getAggregations();
            Map<String, Aggregation> aggregationMap = aggregations.getAsMap();
            Terms terms = (Terms) aggregationMap.get("sku_category");

            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            List<String> categoryList = new ArrayList<>();
            for(Terms.Bucket bucket: buckets){
                categoryList.add(bucket.getKeyAsString());
            }
            resultMap.put("categoryList",categoryList);

            //2.3获取品牌列表，没有选择默认分类的第一个
            String categoryName = "";
            if (searchMap.get("category") == null) {//如果searcMap中有分类
                if (categoryList.size() > 0) { //如果分类列表中有,取第一个分类
                    categoryName = categoryList.get(0);
                }
            } else { //如果searcMap中没有分类
                categoryName = searchMap.get("category");
            }
            if(searchMap.get("brand")==null) {
                List<Map> brandList = (List<Map>) redisTemplate.boundHashOps(CacheKey.CATEGORY_BRAND).get(categoryName);
                if(brandList==null){//先从redis中查询，如果没有，则再从数据库中查询，并且写入到redis
                    System.out.println("从数据库中查询");
                    brandList = brandMapper.findBrandListByCategoryName(categoryName);
                    redisTemplate.boundHashOps(CacheKey.CATEGORY_BRAND).put(categoryName,brandList);
                }else{
//                    System.out.println("从redis中查询");
                }

                resultMap.put("brandList", brandList);
            }

            //2.4获取规格列表
            //先从redis中查询，如果没有，则再从数据库中查询，并且写入到redis
            List<Map> specList = (List<Map>) redisTemplate.boundHashOps(CacheKey.CATEGORY_SPEC).get(categoryName);
            if(specList==null){
                System.out.println("从数据库中查询");
                specList = specMapper.findListByCategoryName(categoryName);
                redisTemplate.boundHashOps(CacheKey.CATEGORY_SPEC).put(categoryName,specList);
            }else{
//                System.out.println("从redis中查询");
            }
            for(Map spec:specList){
                String[] options = ((String) spec.get("options")).split(",");//规格选项列表
                spec.put("options",options);
            }

            resultMap.put("specList",specList);

            //2.5计算总页数
            long totalCount = searchHits.getTotalHits();//总记录数
            long totalPages = (totalCount%pageSize==0)? totalCount/pageSize : (totalCount/pageSize)+1;
            resultMap.put("totalPages",totalPages);

        } catch (IOException e) {
            e.printStackTrace();
        }



        return resultMap;
    }
}
